package com.third.device.mg.serialport.utils.send

import android.app.Application
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import com.third.device.mg.serialport.listener.MGNettyConnectListener
import com.third.device.mg.serialport.service.MGSocketService
import com.third.device.mg.serialport.utils.toHexString
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import io.reactivex.rxjava3.subjects.PublishSubject
import org.jetbrains.annotations.NotNull
import org.json.JSONObject
import java.util.concurrent.TimeUnit

/**
 * @Date 2022/4/7
 * Description:指令发送流程工具类
 * 调用发送指令：
 * 1、自动连接
 * 2、自动重连
 * 3、发送回调
 * 4、接收回调
 */
class MGSendUtils private constructor() : MGNettyConnectListener {
    private lateinit var app: Application

    //是否连接服务
    private var isConnectService = false

    //当前流程状态
    private var processEnum = ProcessEnum.UnConnectService

    //流程观察者
    private val ps = PublishSubject.create<ProcessEnum>()

    private lateinit var psDisposable: Disposable

    //当前操作流程的目标IP
    private var ipStr = ""

    //当前操作流程的目标Port
    private var port = 61233

    //要发送的指令
    private lateinit var sentCommand: ByteArray

    //当前连接次数
    private var connectNum = 0

    //事件监听
    private lateinit var processListener:MGNettyConnectListener


    companion object {
        private const val TAG = "MGSendUtils"
        private const val RECONNECT_NUM = 3 /*重连次数*/
        private const val RECONNECT_TIME_STEP = 1000L /*重连时间间隔ms*/
        val instance = CommandSendUtilsHolder.holder
    }

    private object CommandSendUtilsHolder {
        val holder = MGSendUtils()
    }


    /**
     * 服务端
     */
    private val serviceConnect: ServiceConnection by lazy {
        object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                isConnectService = true
                MGSocketService.get().setListener(this@MGSendUtils)
                ps.onNext(ProcessEnum.ConnectService)
            }

            override fun onServiceDisconnected(name: ComponentName?) {
                isConnectService = false
            }
        }
    }


    /**
     * 订阅流程
     */
    private fun subscribeProcess() {
        psDisposable = ps.map {
            processEnum = it
            processEnum
        }.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { result ->
                when (result) {
                    ProcessEnum.UnConnectService -> {
                        Log.e(TAG, "服务未连接，开始连接服务")
                        bindService()
                    }
                    ProcessEnum.ConnectService -> {
                        Log.e(TAG, "服务已连接，开始检测是否已经连接Netty")
                        checkNetty()
                    }
                    ProcessEnum.UnConnectNetty -> {
                        Log.e(TAG, "Netty未连接，开始连接Netty")
                        connectNetty()
                    }
                    ProcessEnum.ConnectNetty -> {
                        Log.e(TAG, "Netty已连接，开始发送数据")
                        sentDataToNetty()
                    }
                    ProcessEnum.SentOver -> {
                        Log.e(TAG, "发送完毕")

                    }
                    ProcessEnum.SentError -> {
                        Log.e(TAG, "发送异常")

                    }
                    else -> {

                    }
                }
            }

    }

    /**
     * 取消订阅
     */
    private fun disposableProcess() {
        if (this::psDisposable.isInitialized && !this.psDisposable.isDisposed) {
            this.psDisposable.dispose()
        }
    }


    /**
     * 绑定服务
     */
    private fun bindService() {
        val intent = Intent(app, MGSocketService::class.java)
        app.bindService(intent, serviceConnect, Service.BIND_AUTO_CREATE)
    }

    /**
     * 检测是否连接Netty
     */
    private fun checkNetty() {
        val isConnect = MGSocketService.get().checkConnect(ipStr, port)
        ps.onNext(if (isConnect) ProcessEnum.ConnectNetty else ProcessEnum.UnConnectNetty)
    }

    /**
     * 连接netty
     * 重连次数为 [RECONNECT_NUM]  那么如果连续失败 [RECONNECT_NUM] + 1 次，那么就定义为连接失败
     */
    private fun connectNetty() {
        when (connectNum) {
            0 -> {
                Log.e(TAG, "初次连接")
                connectCode()
                ++connectNum
            }
            in 1..RECONNECT_NUM -> {
                Log.e(TAG, "重连第 $connectNum 次")
                Observable.just(connectNum).delay(RECONNECT_TIME_STEP, TimeUnit.MILLISECONDS)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnComplete {
                        connectCode()
                        ++connectNum
                    }
                    .subscribe()
            }
            else -> {
                connectTimeout()
            }
        }
    }

    /**
     * 执行连接的代码
     */
    private fun connectCode() {
        MGSocketService.get().connectNetty(ipStr, port)
    }

    /**
     * 发送消息到Netty
     */
    private fun sentDataToNetty() {
        MGSocketService.get().write(ipStr, port, sentCommand)
    }


    /**
     * 连接超时，估计目标地址异常或者网络中断
     */
    private fun connectTimeout() {
        Log.e(TAG, "连接超时")
        if (this::processListener.isInitialized){
            //processListener.disconnectBack(ipStr, port)
            processListener.errorBack(ipStr ,port , Throwable("连接超时"))
        }
    }


    override fun connectBack(ip: String, port: Int) {
        connectNum = 0
        ps.onNext(ProcessEnum.ConnectNetty)
        if (this::processListener.isInitialized){
            processListener.connectBack(ip, port)
        }
    }

    override fun disconnectBack(ip: String, port: Int) {
        Log.e(TAG, "disconnectBack")
        ps.onNext(ProcessEnum.UnConnectNetty)
    }

    override fun readBack(ip: String, port: Int, result: ByteArray) {
        if (this::processListener.isInitialized){
            processListener.readBack(ip, port ,result)
        }
    }

    override fun writeBack(ip: String, port: Int, successful: Boolean, data: ByteArray) {
        ps.onNext(if (successful) ProcessEnum.SentOver else ProcessEnum.SentError)
        if (this::processListener.isInitialized){
            processListener.writeBack(ip, port, successful, data)
        }
    }

    override fun errorBack(ip: String, port: Int, error: Throwable) {
        if (this::processListener.isInitialized){
            processListener.errorBack(ip, port ,error)
        }

    }


    /*========================================外接调用的方法================================================*/
    /**
     * 初始化指令发送工具类
     * 建议在Application [Application.onCreate]方法调用
     * @param app Application
     */
    fun initUtils(@NotNull app: Application) {
        this.app = app
        //先销毁已有的订阅
        disposableProcess()
        //再重新去订阅
        subscribeProcess()
    }

    /**
     * 初始化指令发送工具类
     * 建议在Application [Application.onCreate]方法调用
     * @param app Application
     * @param listener [MGNettyConnectListener]
     */
    fun initUtils(@NotNull app: Application ,@NotNull listener: MGNettyConnectListener) {
        initUtils(app)
        setMGNettyConnectListener(listener)
    }


    /**
     * Disposable指令发送工具类
     * 建议在Application [Application.onTerminate]方法调用
     */
    fun disposableUtils() {
        disposableProcess()
    }


    /**
     * 取消订阅 指令发送工具类
     * 并断开 指定的Netty
     * @param ip    IP地址
     * @param port  端口号
     */
    fun disposableUtils(@NotNull ip: String, @NotNull port: Int) {
        disposableUtils()
        MGSocketService.get().disconnect(ip ,port)
    }

    /**
     * 设置Netty连接助手
     * @param listener [MGNettyConnectListener]
     */
    fun setMGNettyConnectListener(listener: MGNettyConnectListener){
        this.processListener = listener
    }

    /**
     * 如果目标设备是网关，发送的格式要转成网关的格式
     * @param name     key为name 的value
     * @param command  指令
     */
    fun changeGatewayCommand(name: String, command: ByteArray): ByteArray {
        val data = JSONObject()
        data.put("name", name)
        data.put("data", command.toHexString(false))
        val result = "$data\r\n"
        return result.toByteArray()
    }


    /**
     * 发送指令
     * 1：如果目标设备已经设置到网关了，那么直接Socket连接网关进行TCP通信
     * 2：如果目标设备未设置到网关，那么直接Socket连接设备进行TCP通信
     * @param ip        IP地址
     * @param port     端口号
     * @param command  指令
     */
    fun sentCommand(@NotNull ip: String, @NotNull port: Int, @NotNull command: ByteArray) {
        Log.e(TAG, "收到发送指令的任务")
        this.ipStr = ip
        this.port = port
        this.sentCommand = command
        this.connectNum = 0
        if (isConnectService) {
            ps.onNext(ProcessEnum.ConnectService)
        } else {
            ps.onNext(ProcessEnum.UnConnectService)
        }
    }


}